#include <linux/module.h>
#include <linux/init.h>
#include <linux/regmap.h>
#include <linux/spi/spi.h>
#include <linux/of_gpio.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/delay.h>
#include <linux/irq.h>
#include<linux/interrupt.h>
#include <linux/clk.h>

#include <sound/core.h>
#include <sound/soc.h>
#include <sound/initval.h>


#include "SI3050REGS.h"


/**

dts config
&spi0 {
	si3050@00 {
		compatible = "si,si3050";
		reg = <0>;   //chip select  0:cs0  1:cs1
		id = <0>;
		spi-max-frequency = <24000000>;   //spi output clock
		rst-gpio = <&gpio3 RK_PD1 GPIO_ACTIVE_LOW>;  //optional
		int-gpio = <&gpio0 RK_PD2 IRQ_TYPE_LEVEL_LOW>;
	};
};


*
 */

#define MAX_SI3050_CHIPS 1
struct si3050_data {
	struct device   *dev;
	struct spi_device *spi;
	struct gpio_desc *rst_gpio;
	int irq; // interrupt number
	int n_chip;
	int channels[MAX_SI3050_CHIPS];
	struct clk *mclk;
};


static int dbg_enable = 0;
module_param_named(dbg_level, dbg_enable, int, 0644);

#define DBG(args...) \
	do { \
		if (dbg_enable) { \
			pr_info(args); \
		} \
	} while (0)



static struct si3050_data *si3050 = NULL;
#if 1
static u8 cws[16] = {0x00, 0x08, 0x04, 0x0c, 0x02, 0x0a, 0x06, 0x0e, 0x01, 0x09, 0x05, 0x0d, 0x03, 0x0b, 0x07, 0x0f};
int si3050_write_byte(struct spi_device *spi, u8 chan, u8 addr, u8 dat)
{
	int ret;
	u8 cw = cws[chan];
	cw |= 0x20;
	ret = spi_write(spi, &cw, 1);
	ret = spi_write(spi, &addr, 1);
	ret = spi_write(spi, &dat, 1);
	DBG("%s : 0x%x 0x%x 0x%x, ret=%d\n", __func__, cw, addr, dat, ret);
	return ret;
}

int si3050_read_byte(struct spi_device *spi, u8 chan, u8 addr, u8 *dat)
{
	int ret;
	u8 cw = cws[chan];
	cw |= 0x60; /* Set REG/RAM */
	ret = spi_write(spi, &cw, 1);
	udelay(50);
	printk("%s ret=%d\n", __func__, ret);
	ret = spi_write(spi, &addr, 1);
	udelay(50);
	printk("%s ret=%d\n", __func__, ret);
	ret = spi_read(spi, dat, 1);
	printk("%s ret=%d\n", __func__, ret);
	DBG("%s Send: 0x%x 0x%x Recv: 0x%x, ret=%d\n", __func__, cw, addr, *dat, ret);
	return ret;
}

void ReadRegister(u8 chip_id,u8 channel_id, u8 address, u8 *Data)
{
	int err = 0;
	err = si3050_read_byte(si3050->spi, channel_id, address, Data);
	if(err){
		dev_err(si3050->dev, "%s chip=%d, channel=%d, addr=0x%x, err=%d\n", \
			__func__, chip_id, channel_id, address, err);
	}
}

void si3050_dump(void)
{
	int chip_id, chan, i;
	u8 dat;
	dev_info(si3050->dev, "si3050 dump chip_count=%d\n", si3050->n_chip);
	for(chip_id=0;chip_id<si3050->n_chip;chip_id++){
		dev_info(si3050->dev, "Register for chip_id=%d, chan=%d\n", chip_id, si3050->channels[chip_id]);
		for(chan=0; chan < si3050->channels[chip_id]; chan++){
			for(i=0; i<=SPRK_QNCH_CTRL; i++){
				ReadRegister(chip_id, chan, i, &dat);
				dev_info(si3050->dev, "Reg:0x%x:0x%x", i, dat);
			}
		}
	}
}



void WriteRegister(u8 chip_id,u8 channel_id, u8 address, u8 Data)
{
	int err = 0;
	err = si3050_write_byte(si3050->spi, channel_id, address, Data);
	if(err){
		dev_err(si3050->dev, "%s chip=%d, channel=%d, addr=0x%x, data=0x%x\n", \
			__func__, chip_id, channel_id, address, Data);
	}
}

void Si3050_init(u8 chip_id,u8 channel_id)
{
	WriteRegister(chip_id,channel_id,INTL_CTRL1,0x11);  //Register 16 = 11  # IIR Filter	RT=1, 19.35 Vrms 23.65 VRMS
	WriteRegister(chip_id,channel_id,INTL_CTRL2,0x02);
	WriteRegister(chip_id,channel_id,DC_TERM_CTRL,0xC0);  //Register 26 = C0  # DCV=11 MINI=00
	WriteRegister(chip_id,channel_id,DAA_CTRL5,0xa2);  //#Register 31 = A2	 # FULL=1 FOH=128ms  FILT=1 HPF2=0 (RX=180Hz)  =a3可解决反向摘机时，极性错误，但开短路挂机不为0
	WriteRegister(chip_id,channel_id,DAA_CTRL1,0x08);	 //		 #on-hk
	/*############铃流校验#################*/
	WriteRegister(chip_id,channel_id,RNG_VLD_CTRL1,0x16);
	WriteRegister(chip_id,channel_id,RNG_VLD_CTRL2,0x09);
	WriteRegister(chip_id,channel_id,RNG_VLD_CTRL3,0x99);
	/*########### R200_680_104  #######*/
	//WriteRegister(chip_id,channel_id,AC_TERM_CTRL,0x0a);
	//WriteRegister(chip_id,channel_id,TX_GN_CTRL3,0x15);
	//AD:0DB  DA:-3.5
	//WriteRegister(chip_id,channel_id,TX_GN_CTRL2,0x12);
	//WriteRegister(chip_id,channel_id,TX_GN_CTRL3,0x15);
	//WriteRegister(chip_id,channel_id,RX_GN_CTRL2,0x00);
	//WriteRegister(chip_id,channel_id,RX_GN_CTRL3,0x08);

	WriteRegister(chip_id,channel_id,SPRK_QNCH_CTRL,0x00);  //On-Hook Speed,Less than 0.5 ms
	WriteRegister(chip_id,channel_id,DAA_CTRL2,0x00);	 //Register 6 = 00		 # power up lineside
	//WriteRegister(chip_id,channel_id,DAA_CTRL1,0x09);	 //		 #on-hk
	WriteRegister(chip_id,channel_id,DAA_CTRL1,0x08);	 //		 #off-hk
	//printf ("init si350.......\n\n");
}


void init_8FXO(struct si3050_data * priv)
{
	int chip_id, chan;
	for(chip_id=0;chip_id<priv->n_chip;chip_id++){
		for(chan=0; chan < priv->channels[chip_id]; chan++){
			WriteRegister(chip_id,chan,CTRL1,0x80); //Register 1 = 80		 # reset
		}
	}
	msleep(100);  //WAIT 100
	for(chip_id=0;chip_id<priv->n_chip;chip_id++){
		for(chan=0; chan < priv->channels[chip_id]; chan++){
			Si3050_init(chip_id,chan);
		}
	}
}
void KT143B_PCM_set(struct si3050_data * priv)
{
	int chip_id, chan;
	/*pcm mode*/
	for(chip_id=0;chip_id<priv->n_chip;chip_id++){
		for(chan=0; chan < priv->channels[chip_id]; chan++){
			WriteRegister(chip_id,chan,PCM_SPI_CTRL,0x20);
		}
	}
	WriteRegister(0,0,34,0x00);
	WriteRegister(0,0,36,0x08);                //ch1fxo

	WriteRegister(0,1,34,0x08);
	WriteRegister(0,1,36,0x00);                //ch2 fxo

	// WriteRegister(1,1,34,0x00);
	// WriteRegister(1,1,36,0x08);                //ch1fxo

	// WriteRegister(1,2,34,0x08);
	// WriteRegister(1,2,36,0x00);                //ch2 fxo

	// WriteRegister(2,1,34,0x10);
	// WriteRegister(2,1,36,0x18);                 //ch3 fxo

	// WriteRegister(2,2,34,0x18);
	// WriteRegister(2,2,36,0x10);                 //ch4 fxo

	// WriteRegister(3,1,34,0x20);
	// WriteRegister(3,1,36,0x28);                //ch5fxo

	// WriteRegister(3,2,34,0x28);
	// WriteRegister(3,2,36,0x20);                //ch6 fxo

	// WriteRegister(4,1,34,0x30);
	// WriteRegister(4,1,36,0x38);                 //ch7 fxo

	// WriteRegister(4,2,34,0x38);
	// WriteRegister(4,2,36,0x30);                 //ch8 fxo
}
#endif
void si3050_setup(struct si3050_data * priv)
{
	gpiod_set_value(priv->rst_gpio, 1);
	msleep(50);
	gpiod_set_value(priv->rst_gpio, 0);
	init_8FXO(priv);
	KT143B_PCM_set(priv);
}

static irqreturn_t si3050_irq(int irq, void *dev_id)
{
	struct si3050_data * priv = dev_id;
	dev_info(priv->dev, "%s called\n", __func__);
//	disable_irq_nosync(priv->irq);
	return IRQ_HANDLED;
}

// si3050 codec related @{
static int si3050_codec_probe(struct snd_soc_codec *codec)
{
	// TODO: add your operation
	dev_info(codec->dev, "%s\n", __func__);
	return 0;
}

static int si3050_codec_remove(struct snd_soc_codec *codec)
{
	// TODO: add your operation
	dev_info(codec->dev, "%s\n", __func__);
	return 0;
}

static struct snd_soc_codec_driver soc_codec_device_si3050 = {
	.probe =	si3050_codec_probe,
	.remove =	si3050_codec_remove,
};


int si3050_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir)
{
	struct snd_soc_codec *codec = codec_dai->codec;
	struct device * dev = codec->dev;
//	struct si3050_data *priv = snd_soc_codec_get_drvdata(codec);

	dev_info(dev, "clk_id=%d, freq=%u, dir=%d\n", clk_id, freq, dir);
	return 0;
}

static int si3050_set_dai_fmt(struct snd_soc_dai *codec_dai,
			      unsigned int format)
{
	struct snd_soc_codec *codec = codec_dai->codec;
	struct device * dev = codec->dev;
	dev_info(dev, "%s format=%d\n", __func__, format);
	return 0;
}

static int si3050_hw_params(struct snd_pcm_substream *substream,
			    struct snd_pcm_hw_params *params,
			    struct snd_soc_dai *codec_dai)
{
	struct snd_soc_pcm_runtime *rtd = substream->private_data;
	struct snd_soc_codec *codec = rtd->codec;
	struct device * dev = codec->dev;

	dev_info(dev, "%s\n", __func__);
	return 0;
}

static struct snd_soc_dai_ops si3050_dai_ops = {
	.hw_params = si3050_hw_params,
	.set_fmt = si3050_set_dai_fmt,
	.set_sysclk = si3050_set_dai_sysclk,
};

#define SI3050_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 )

static struct snd_soc_dai_driver si3050_dai = {
	.name = "si3050 GvDAA",
	.playback = {
		.stream_name = "Playback",
		.channels_min = 1,
		.channels_max = 2,
		.rates = SI3050_RATES,
		.formats =SNDRV_PCM_FMTBIT_S16_LE
	},
	.capture = {
		.stream_name = "Capture",
		.channels_min = 1,
		.channels_max = 2,
		.rates = SI3050_RATES,
		.formats = SNDRV_PCM_FMTBIT_S16_LE,
	},
	.ops = &si3050_dai_ops,
};
// si3050 codec related @}

// si3050 sysfs interface @{

static ssize_t si3050_show(struct device *dev,struct device_attribute *attr, char *buf)
{
	int n = 0;
	int chip_id, chan, i;
	u8 dat=0;
	n = sprintf(buf, "si3050 dump chip_count=%d\n", si3050->n_chip);
	for(chip_id=0;chip_id<si3050->n_chip;chip_id++){
		n += sprintf(buf+n,  "Register for chip_id=%d, chan=%d\n", chip_id, si3050->channels[chip_id]);
		for(chan=0; chan < si3050->channels[chip_id]; chan++){
			for(i=0; i<=SPRK_QNCH_CTRL; i++){
				ReadRegister(chip_id, chan, i, &dat);
				n += sprintf(buf+n,  "Reg:0x%x:0x%x\n", i, dat);
			}
		}
	}
	return n;
}

static ssize_t si3050_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count)
{
	int chip, chan, addr, val;
	u8 dat;
	int ret = 0;

	ret = sscanf(buf, "%d %d %d %d", &chip, &chan, &addr, &val);
	if(ret < 4){
		ret = sscanf(buf, "%d %d %d", &chip, &chan, &addr);
		if(ret == 3){
			ReadRegister(chip, chan, addr, &dat);
			dev_info(dev, "%s read 0x%x: 0x%x\n", __func__, addr, dat);
		}else{
			dev_err(dev, "Input Error: ret=%d \n", ret);
			return -EINVAL;
		}

	}else{
		WriteRegister(chip, chan, addr, val);
		ReadRegister(chip, chan, addr, &dat);
		dev_info(dev, "%s write 0x%x: 0x%x, readback: 0x%x\n", __func__, addr, val, dat);
	}
	return count;
}

static DEVICE_ATTR(debug, 0644, si3050_show, si3050_store);
// si3050 sysfs interface @}



void parse_dt(struct spi_device *spi)
{
	int err=0;
	int irq_pin = 0;
	struct device *dev =  &spi->dev;
	struct device_node * np = dev->of_node;
	struct si3050_data * priv = dev_get_drvdata(dev);


	priv->rst_gpio = devm_gpiod_get_optional(dev, "rst",GPIOD_OUT_LOW);
	if(IS_ERR(priv->rst_gpio)){
		err = PTR_ERR(priv->rst_gpio);
		dev_err(dev, "Unable to claim gpio rst err=%d\n", err);
	}

	irq_pin = of_get_named_gpio_flags(np, "int-gpio", 0, NULL);
	if (!gpio_is_valid(irq_pin)) {
		dev_err(dev, "get int-gpio fail\n");
	}else{
		priv->irq = gpio_to_irq(irq_pin);
		if (priv->irq == 0) { /* temperary disable it */
			err = devm_request_threaded_irq(dev, priv->irq, NULL, si3050_irq, IRQF_TRIGGER_LOW | IRQF_ONESHOT, "si3050", priv);
			if (err != 0) {
				dev_err(dev, "Cannot allocate si3050 INT!ERRNO:%d\n", err);
			}
		//	disable_irq(priv->irq);
		} else {
			dev_err(dev, " gpio_to_irq fail \n");
		}
	//	enable_irq(ts->irq);
	}
	priv->n_chip = 1;
	priv->channels[0] = 1;

	// parse clock related
	priv->mclk = devm_clk_get(dev, "mclk");
	if(IS_ERR(priv->mclk)){
		dev_err(dev, "devm_clk_get mclk failed, err=%ld\n", PTR_ERR(priv->mclk));
		priv->mclk = NULL;
	}
}

static void si3050_hw_setup(struct si3050_data * priv)
{
	int err = 0;
	struct device * dev = priv->dev;
	gpiod_set_value(priv->rst_gpio, 0);
	msleep(1);
	gpiod_set_value(priv->rst_gpio, 1);
	clk_set_rate(priv->mclk,2097152);  // 2.04M, no work
	err = clk_prepare_enable(priv->mclk);
	if(err){
		dev_err(dev, "clk_prepare_enable priv->mclk failed err=%d\n", err);
	}
	dev_info(dev, "mclk rate=%ld\n", clk_get_rate(priv->mclk));
	msleep(5);
	gpiod_set_value(priv->rst_gpio, 0);
}

static int si3050_probe(struct spi_device *spi)
{
	int err = 0;
	struct si3050_data * priv;

	if((!spi) || (!spi->dev.of_node)){
		return -ENOMEM;
	}
	spi->bits_per_word = 8;
	err = spi_setup(spi);
	if(err < 0){
		dev_err(&spi->dev, "ERR: fail to setup spi\n");
		return err;
	}
	priv = kzalloc(sizeof(struct si3050_data), GFP_KERNEL);
	if(!priv){
		dev_err(&spi->dev, "ERR: no memory for si3050_data");
		return -ENOMEM;
	}
	si3050 = priv;
	dev_set_drvdata(&spi->dev, priv);
	priv->dev = &spi->dev;
	priv->spi = spi;
	parse_dt(spi);
	//si3050_setup(priv);
	si3050_hw_setup(priv);
	si3050_dump();
	err = device_create_file(&spi->dev, &dev_attr_debug);
	if(err) {
		dev_err(&spi->dev, "device_create_file fail err=%d\n", err);
	}

	// err = snd_soc_register_codec(&spi->dev,
	// 		&soc_codec_device_si3050, &si3050_dai, 1);
	if(err < 0) {
		dev_err(&spi->dev, "snd_soc_register_codec fail, err=%d\n", err);
	}
	dev_info(&spi->dev, "%s return err=%d\n", __func__, err);
	return err;
}

static int si3050_remove(struct spi_device *spi)
{
	struct si3050_data * priv = dev_get_drvdata(&spi->dev);
	dev_info(&spi->dev, "%s\n", __func__);
	snd_soc_unregister_codec(&spi->dev);
	device_remove_file(&spi->dev, &dev_attr_debug);
	if(priv !=NULL){
		if(priv->mclk){
			clk_disable_unprepare(priv->mclk);
		}
		kfree(priv);
		priv = NULL;
	}
	return 0;
}

static const struct spi_device_id si3050_spi_id[] = {
	{ "si3050", },
	{ },
};
MODULE_DEVICE_TABLE(spi, si3050_spi_id);

static const struct of_device_id si5030_dt_match[] = {
	{.compatible="si,si3050"},
	{},
};

MODULE_DEVICE_TABLE(of, si5030_dt_match);

static struct spi_driver spi_si3050_driver = {
	.probe = si3050_probe,
	.remove = si3050_remove,
	.id_table	= si3050_spi_id,
	.driver = {
		.name = "si3050",
		.owner = THIS_MODULE,
		.of_match_table = of_match_ptr(si5030_dt_match),
	},

};


static int __init spi_si3050_init(void)
{
	int err = 0;
	err = spi_register_driver(&spi_si3050_driver);
	printk("%s return with %d\n", __func__, err);
	return err;
}

static void __exit spi_si3050_exit(void)
{

	spi_unregister_driver(&spi_si3050_driver);
}


module_init(spi_si3050_init);
module_exit(spi_si3050_exit);


MODULE_AUTHOR("tj<tj_wust@163.com>");
MODULE_LICENSE("GPL");


